package com.benjaminwan.seriallibrary

import android.util.Log
import java.io.*

class SerialPort {
    companion object {
        const val TAG = "SerialPort"
        private const val DEFAULT_SU_PATH = "/system/bin/su"
        var suPath = DEFAULT_SU_PATH
    }

    init {
        System.loadLibrary("SerialLibrary")
    }

    var serialDevice: SerialDevice? = null
    private var mFd: FileDescriptor? = null
    var inputStream: FileInputStream? = null
    var outputStream: FileOutputStream? = null

    /**
     * 打开串口
     *
     * @param device 串口设备文件
     * @param baudRate 波特率
     * @param parity 奇偶校验，0 None（默认）； 1 Odd； 2 Even
     * @param dataBits 数据位，5 ~ 8  （默认8）
     * @param stopBit 停止位，1 或 2  （默认 1）
     * @param flags 标记 0（默认）
     * @throws SecurityException
     * @throws IOException
     */
    @Throws(SecurityException::class, IOException::class)
    @JvmOverloads
    fun open(
        device: SerialDevice,
        baudRate: Int,
        parity: Int = 0,
        dataBits: Int = 8,
        stopBit: Int = 1,
        flags: Int = 0
    ) {
        /* Check access permission */
        if (!device.file.canRead() || !device.file.canWrite()) {
            Log.d(TAG, "Missing read/write permission, trying to chmod the file")
            try {
                /* Missing read/write permission, trying to chmod the file */
                val su: Process = Runtime.getRuntime().exec(suPath)
                val cmd = "chmod 666 ${device.file.absolutePath}\nexit\n".trimIndent()
                su.outputStream.write(cmd.toByteArray())
                if (su.waitFor() != 0 || !device.file.canRead() || !device.file.canWrite()) {
                    throw SecurityException()
                }
            } catch (e: Exception) {
                e.printStackTrace()
                throw SecurityException()
            }
        }
        mFd = open(device.file.absolutePath, baudRate, parity, dataBits, stopBit, flags)
        if (mFd == null) {
            Log.e(TAG, "native open returns null")
            throw IOException()
        }
        inputStream = FileInputStream(mFd)
        outputStream = FileOutputStream(mFd)
        serialDevice = device
    }

    val isOpened: Boolean
        get() = null != mFd && null != inputStream && null != outputStream

    val isClosed: Boolean
        get() = !isOpened

    fun close() {
        if (null != mFd) {
            closeFd()
            mFd = null
        }
        if (null != inputStream) {
            try {
                inputStream?.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
            inputStream = null
        }
        if (null != outputStream) {
            try {
                outputStream?.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
            outputStream = null
        }
        serialDevice = null
    }

    fun read() = inputStream?.read()

    fun read(output: ByteArray) = inputStream?.read(output)

    fun read(output: ByteArray, offset: Int, len: Int) = inputStream?.read(output, offset, len)

    fun write(data: Int) = outputStream?.write(data)

    fun write(data: ByteArray) = outputStream?.write(data)

    fun write(data: ByteArray, offset: Int, len: Int) = outputStream?.write(data, offset, len)

    private external fun open(
        path: String, baudRate: Int, parity: Int, dataBits: Int,
        stopBit: Int, flags: Int
    ): FileDescriptor?

    private external fun closeFd()
}